﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Security;
using System.Runtime.InteropServices;

namespace DS2171Utils
{
   public  class Encryptor
    {
       public Encryptor()
       {

       }

       public  byte[] Encrypt(byte[] clearData, string Password)
       {
           var pdb = new Rfc2898DeriveBytes(Password,
               new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});

           return Encrypt(clearData, pdb.GetBytes(32), pdb.GetBytes(16));

       }//fM

       public  byte[] Encrypt(byte[] clearData, byte[] Key, byte[] IV)
       {
           MemoryStream ms = new MemoryStream();

           Rijndael alg = Rijndael.Create();

           alg.Key = Key;
           alg.IV = IV;

           CryptoStream cs = new CryptoStream(ms,alg.CreateEncryptor(), CryptoStreamMode.Write);

           cs.Write(clearData, 0, clearData.Length);

           cs.Close();

            byte[] encryptedData = ms.ToArray();

           return encryptedData;
       }
       
       public  void Encrypt(string fileIn, string fileOut, string Password) 
    { 

        // First we are going to open the file streams 

        FileStream fsIn = new FileStream(fileIn, 
            FileMode.Open, FileAccess.Read); 
        FileStream fsOut = new FileStream(fileOut, 
            FileMode.OpenOrCreate, FileAccess.Write); 

        // Then we are going to derive a Key and an IV from the

        // Password and create an algorithm 

        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(Password, 
            new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76}); 

        Rijndael alg = Rijndael.Create(); 
        alg.Key = pdb.GetBytes(32); 
        alg.IV = pdb.GetBytes(16); 

        // Now create a crypto stream through which we are going

        // to be pumping data. 

        // Our fileOut is going to be receiving the encrypted bytes. 

        CryptoStream cs = new CryptoStream(fsOut, 
            alg.CreateEncryptor(), CryptoStreamMode.Write); 

        // Now will will initialize a buffer and will be processing

        // the input file in chunks. 

        // This is done to avoid reading the whole file (which can

        // be huge) into memory. 

        int bufferLen = 4096; 
        byte[] buffer = new byte[4096]; 
        int bytesRead; 

        do { 
            // read a chunk of data from the input file 

            bytesRead = fsIn.Read(buffer, 0, bufferLen); 

            // encrypt it 

            cs.Write(buffer, 0, bytesRead); 
        } while(bytesRead != 0); 

        // close everything 


        // this will also close the unrelying fsOut stream

        cs.Close(); 
        fsIn.Close();     
    }
       
       public  string Encrypt(string clearText, string Password)
        {
            // First we need to turn the input string into a byte array. 

            byte[] clearBytes =
              System.Text.Encoding.Unicode.GetBytes(clearText);

            // Then, we need to turn the password into Key and IV 

            // We are using salt to make it harder to guess our key

            // using a dictionary attack - 

            // trying to guess a password by enumerating all possible words. 

            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});

            // Now get the key/IV and do the encryption using the

            // function that accepts byte arrays. 

            // Using Rfc2898DeriveBytes object we are first getting

            // 32 bytes for the Key 

            // (the default Rijndael key length is 256bit = 32bytes)

            // and then 16 bytes for the IV. 

            // IV should always be the block size, which is by default

            // 16 bytes (128 bit) for Rijndael. 

            // If you are using DES/TripleDES/RC2 the block size is

            // 8 bytes and so should be the IV size. 

            // You can also read KeySize/BlockSize properties off

            // the algorithm to find out the sizes. 

            byte[] encryptedData = Encrypt(clearBytes,
                     pdb.GetBytes(32), pdb.GetBytes(16));

            // Now we need to turn the resulting byte array into a string. 

            // A common mistake would be to use an Encoding class for that.

            //It does not work because not all byte values can be

            // represented by characters. 

            // We are going to be using Base64 encoding that is designed

            //exactly for what we are trying to do. 

            return Convert.ToBase64String(encryptedData);

        }
           
       public  byte[] Decrypt(byte[] cipherData, byte[] Key, byte[] IV)
       {
           MemoryStream ms = new MemoryStream();

           // Create a symmetric algorithm. 

           // We are going to use Rijndael because it is strong and available on all platforms. 

           // You can use other algorithms, to do so substitute the next

           // line with something like  TripleDES alg = TripleDES.Create(); 

           Rijndael alg = Rijndael.Create();

           alg.Key = Key;
           alg.IV = IV;

           CryptoStream cs = new CryptoStream(ms,alg.CreateDecryptor(), CryptoStreamMode.Write);

           cs.Write(cipherData, 0, cipherData.Length);

           cs.Close();

           byte[] decryptedData = ms.ToArray();

           return decryptedData;
       }

       public  byte[] Decrypt(byte[] cipherData, string Password)
       {
           var pdb = new Rfc2898DeriveBytes(Password,
               new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});

           // Now get the key/IV and do the Decryption using the 

           //function that accepts byte arrays. 

           // Using Rfc2898DeriveBytes object we are first getting

           // 32 bytes for the Key 

           // (the default Rijndael key length is 256bit = 32bytes)

           // and then 16 bytes for the IV. 

           // IV should always be the block size, which is by default

           // 16 bytes (128 bit) for Rijndael. 

           // If you are using DES/TripleDES/RC2 the block size is

           // 8 bytes and so should be the IV size. 


           // You can also read KeySize/BlockSize properties off the

           // algorithm to find out the sizes. 

           return Decrypt(cipherData, pdb.GetBytes(32), pdb.GetBytes(16));
       }

       public  void Decrypt(string fileIn, string fileOut, string Password) 
        { 
    
        // First we are going to open the file streams 

        FileStream fsIn = new FileStream(fileIn,
                    FileMode.Open, FileAccess.Read); 
        FileStream fsOut = new FileStream(fileOut,
                    FileMode.OpenOrCreate, FileAccess.Write); 
  
        // Then we are going to derive a Key and an IV from

        // the Password and create an algorithm 

        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(Password, 
            new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76}); 
        Rijndael alg = Rijndael.Create(); 

        alg.Key = pdb.GetBytes(32); 
        alg.IV = pdb.GetBytes(16); 

        // Now create a crypto stream through which we are going

        // to be pumping data. 

        // Our fileOut is going to be receiving the Decrypted bytes. 

        CryptoStream cs = new CryptoStream(fsOut, 
            alg.CreateDecryptor(), CryptoStreamMode.Write); 
  
        // Now will will initialize a buffer and will be 

        // processing the input file in chunks. 

        // This is done to avoid reading the whole file (which can be

        // huge) into memory. 

        int bufferLen = 4096; 
        byte[] buffer = new byte[4096]; 
        int bytesRead; 

        do { 
            // read a chunk of data from the input file 

            bytesRead = fsIn.Read(buffer, 0, bufferLen); 

            // Decrypt it 

            cs.Write(buffer, 0, bytesRead); 

        } while(bytesRead != 0); 

        // close everything 

        cs.Close(); // this will also close the unrelying fsOut stream 

        fsIn.Close();     
    }
       
       public  string Decrypt(string cipherText, string Password)
        {
            // First we need to turn the input string into a byte array. 

            // We presume that Base64 encoding was used 

            byte[] cipherBytes = Convert.FromBase64String(cipherText);

            // Then, we need to turn the password into Key and IV 

            // We are using salt to make it harder to guess our key

            // using a dictionary attack - 

            // trying to guess a password by enumerating all possible words. 

            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 
            0x64, 0x76, 0x65, 0x64, 0x65, 0x76});

            // Now get the key/IV and do the decryption using

            // the function that accepts byte arrays. 

            // Using Rfc2898DeriveBytes object we are first

            // getting 32 bytes for the Key 

            // (the default Rijndael key length is 256bit = 32bytes)

            // and then 16 bytes for the IV. 

            // IV should always be the block size, which is by

            // default 16 bytes (128 bit) for Rijndael. 

            // If you are using DES/TripleDES/RC2 the block size is

            // 8 bytes and so should be the IV size. 

            // You can also read KeySize/BlockSize properties off

            // the algorithm to find out the sizes. 

            byte[] decryptedData = Decrypt(cipherBytes,
                pdb.GetBytes(32), pdb.GetBytes(16));

            // Now we need to turn the resulting byte array into a string. 

            // A common mistake would be to use an Encoding class for that.

            // It does not work 

            // because not all byte values can be represented by characters. 

            // We are going to be using Base64 encoding that is 

            // designed exactly for what we are trying to do. 

            return System.Text.Encoding.Unicode.GetString(decryptedData);
        }


       public SecureString transformInSecureStringReadOnly(string pDatoSensibile)
       { 
           SecureString ss = new SecureString();
           //inserisce il valore di s in ss un carattere per volta
           for (int i = 0; i < pDatoSensibile.Length; i++)
               ss.AppendChar(pDatoSensibile[i]);
           //può essere utile rendere il dato criptato in modalità "sola lettura"
           ss.MakeReadOnly();
           return ss;
       }//fM

       public string readStringFromSecureString(SecureString pSecStr)
       {
           IntPtr p = Marshal.SecureStringToBSTR(pSecStr);
            string x = Marshal.PtrToStringBSTR(p);
            Marshal.ZeroFreeBSTR(p);
            return x;
       }//fM


    }//fC
}
